/****************************************************************************************
**  File: Window.h
**  Author: Asteroth
**  Date: Apr 27, 2009
****************************************************************************************/

#ifndef __WINDOW_HPP__
#define __WINDOW_HPP__

//#include <fstream> // for usleep()

const uint KEYBOARD_BUTTON_COUNT = 256;
const uint MOUSE_BUTTON_COUNT = 8;

#if defined(LINUX) /// X11 ======================================================================

#include <X11/X.h>
#include <X11/keysym.h>
#include <X11/extensions/xf86vmode.h>

#define __USE_OPENGL_3_0__
#define __USE_FORWARD_COMPATIBLE_OPENGL_3_0__

enum eKeyStateMask
{
    ksNone              = 0,
    ksShift             = 1,
    ksCapsLock          = 2,
    ksControl           = 4,
    ksAlternate         = 8,
    ksNumLock           = 16,
    ksArchi             = 32,       // No idea
    ksSuper             = 64,       // Win
    ksHyper             = 128       // AltGr
};

enum eKey
{
    kEscape             = 9,
    k1                  = 10,
    k2                  = 11,
    k3                  = 12,
    k4                  = 13,
    k5                  = 14,
    k6                  = 15,
    k7                  = 16,
    k8                  = 17,
    k9                  = 18,
    k0                  = 19,
    kMinus              = 20,
    kEqual              = 21,
    kBackspace          = 22,
    kTab                = 23,
    kQ                  = 24,
    kW                  = 25,
    kE                  = 26,
    kR                  = 27,
    kT                  = 28,
    kY                  = 29,
    kU                  = 30,
    kI                  = 31,
    kO                  = 32,
    kP                  = 33,
    kOpenBracket        = 34,
    kCloseBracket       = 35,
    kEnter              = 36,
    kLControl           = 37,
    kA                  = 38,
    kS                  = 39,
    kD                  = 40,
    kF                  = 41,
    kG                  = 42,
    kH                  = 43,
    kJ                  = 44,
    kK                  = 45,
    kL                  = 46,
    kQuote              = 47,
    kDblQuote           = 48,
    kBackQuote          = 49,
    kLShift             = 50,
    kBackSlash          = 51,
    kZ                  = 52,
    kX                  = 53,
    kC                  = 54,
    kV                  = 55,
    kB                  = 56,
    kN                  = 57,
    kM                  = 58,
    kSemicolon          = 59,
    kColon              = 60,
    kSlash              = 61,
    kRShift             = 62,
    kNumStar            = 63,
    kAlt                = 64,
    kSpace              = 65,
    kCapsLock           = 66,
    kF1                 = 67,
    kF2                 = 68,
    kF3                 = 69,
    kF4                 = 70,
    kF5                 = 71,
    kF6                 = 72,
    kF7                 = 73,
    kF8                 = 74,
    kF9                 = 75,
    kF10                = 76,
    kNumLock            = 77,
    kScrollLock         = 78,
    kNum7               = 79,
    kNum8               = 80,
    kNum9               = 81,
    kNumMinus           = 82,
    kNum4               = 83,
    kNum5               = 84,
    kNum6               = 85,
    kNumPlus            = 86,
    kNum1               = 87,
    kNum2               = 88,
    kNum3               = 89,
    kNum0               = 90,
    kNumDecimal         = 91,

    kF11                = 95,
    kF12                = 96,
    kHome               = 97,
    kUp                 = 98,
    kPageUp             = 99,
    kLeft               = 100,

    kRight              = 102,
    kEnd                = 103,
    kDown               = 104,
    kPageDown           = 105,
    kInsert             = 106,
    kDelete             = 107,

    kRControl           = 109,

    kNumSlash           = 112,
    kHyper              = 113,

    kSuper              = 115,

    kMenu               = 117
};

enum eButton
{
    bLeft               = 1,
    bMiddle             = 2,
    bRight              = 3
};

class eWindow
{
    private:
        int winX, winY;
        uint winWidth, winHeight;
        uint depth;

        // context
        bool contextInitialized;
        Display *display;
        int screen;
        XF86VidModeModeInfo **modes;
        int modeCount;
        XVisualInfo *visual;
        GLXContext renderContext;
        Colormap colorMap;

        // window
        char* caption;
        Window window;
        XSetWindowAttributes windowAttributes;
        int bestMode;


        Atom wmDelete;
        Window winDummy;
        unsigned int borderDummy;

        bool ready;
        bool doubleBuffering;
        bool fullscreen;

        XEvent event;
        bool drawed;

        bool keyH[KEYBOARD_BUTTON_COUNT], keyD[KEYBOARD_BUTTON_COUNT];
        char mouseH[MOUSE_BUTTON_COUNT], mouseD[MOUSE_BUTTON_COUNT];
        int cursX, cursY;

    public:
        eWindow()
        {
            this->caption = NULL;
            window = NULL;

            contextInitialized = false;
            display = NULL;
            visual = NULL;

            for (uint i=0; i<KEYBOARD_BUTTON_COUNT; i++)
                keyH[i] = keyD[i] = false;
            for (uint i=0; i<MOUSE_BUTTON_COUNT; i++)
                mouseH[i] = mouseD[i] = 0;

            doubleBuffering = false;
            ready = false;
            //open(caption, winX, winY, winWidth, winHeight, depth, fullscreen);
        }

        ~eWindow()
        {
            close();
        }

        bool open(
            const char* caption = "uuWindow",
            int winX = 0, int winY = 0,
            uint winWidth = 800, uint winHeight = 600,
            uint depth = 16, bool fullscreen = false)
        {
            if (ready)
                close();

            if (!contextInitialized)
            {
                int dpth = depth==32?24:depth==24?24:16;
                int bpp = dpth==32?8:dpth==24?8:5;

                int sglBuff[] = {
                    GLX_RGBA,
                    GLX_RED_SIZE, bpp,
                    GLX_GREEN_SIZE, bpp,
                    GLX_BLUE_SIZE, bpp,
                    GLX_DEPTH_SIZE, dpth,
                    None };

                int dblBuff[] = {
                    GLX_RGBA, GLX_DOUBLEBUFFER,
                    GLX_RED_SIZE, bpp,
                    GLX_GREEN_SIZE, bpp,
                    GLX_BLUE_SIZE, bpp,
                    GLX_DEPTH_SIZE, dpth,
                    None };

                display = XOpenDisplay(0);
                screen = DefaultScreen(display);

                XF86VidModeGetAllModeLines(display, screen, &modeCount, &modes);

                visual = glXChooseVisual(display, screen, dblBuff);
                if (visual == NULL)
                {
                    visual = glXChooseVisual(display, screen, sglBuff);
                    doubleBuffering = false;
                    if (visual == NULL)
                        return false;
                }
                else
                    doubleBuffering = true;

            #ifdef __USE_OPENGL_3_0__
                typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
                int nelements;
                GLXFBConfig *fbc = glXChooseFBConfig(display, DefaultScreen(display), 0, &nelements);

                GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
                if (glXCreateContextAttribsARB!=NULL && fbc!=NULL)
                {
                    #define GLX_CONTEXT_MAJOR_VERSION_ARB		        0x2091
                    #define GLX_CONTEXT_MINOR_VERSION_ARB		        0x2092
                    #define GLX_CONTEXT_FLAGS_ARB			            0x2094
                    #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB	    0x0002

                    int attribs[] = {
                        GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
                        GLX_CONTEXT_MINOR_VERSION_ARB, 0,
                    #ifdef __USE_FORWARD_COMPATIBLE_OPENGL_3_0__
                        GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
                    #endif
                        0};

                    //GLXContext tempRC = renderContext;
                    renderContext = glXCreateContextAttribsARB(display, *fbc, 0, true, attribs);
                }
                else
            #endif
                    renderContext = glXCreateContext(display, visual, 0, GL_TRUE);

                if (!renderContext)
                    return false;

                colorMap = XCreateColormap(display, RootWindow(display, visual->screen), visual->visual, AllocNone);

                contextInitialized = true;
            }


            delete[] this->caption;
            this->caption = new char[strlen(caption)+1];
            strcpy(this->caption, caption);

            // choose the best mode for requested resolution (used in fullscreen only)
            bestMode = 0;
            for (int i = 0; i < modeCount; i++)
                if (modes[i]->hdisplay == winWidth && modes[i]->vdisplay == winHeight)
                {
                    bestMode = i;
                    break;
                }



            windowAttributes.colormap = colorMap;
            windowAttributes.border_pixel = 0;
            windowAttributes.event_mask =
                  KeyPressMask
                | KeyReleaseMask
                | ButtonPressMask
                | ButtonReleaseMask
                | PointerMotionMask
                | ExposureMask
                | StructureNotifyMask;
            windowAttributes.override_redirect = True;

            if (fullscreen)
            {
                XF86VidModeSwitchToMode(display, screen, modes[bestMode]);
                XF86VidModeSetViewPort(display, screen, 0, 0);

                window = XCreateWindow(display, RootWindow(display, visual->screen),
                    0, 0, modes[bestMode]->hdisplay, modes[bestMode]->vdisplay, InputOutput, visual->depth, InputOutput, visual->visual,
                    CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &windowAttributes);

                XWarpPointer(display, None, window, 0, 0, 0, 0, 0, 0);
        		XMapRaised(display, window);

                XGrabKeyboard(display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime);
                XGrabPointer(display, window, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
            }
            else
            {
                window = XCreateWindow(display, RootWindow(display, visual->screen),
                    0, 0, winWidth, winHeight, 0, visual->depth, InputOutput, visual->visual,
                    CWBorderPixel | CWColormap | CWEventMask, &windowAttributes);

                wmDelete = XInternAtom(display, "WM_DELETE_WINDOW", True);
                XSetWMProtocols(display, window, &wmDelete, 1);
                XSetStandardProperties(display, window, caption, caption, None, NULL, 0, NULL);
                XMapRaised(display, window);
            }

            glXMakeCurrent(display, window, renderContext);

            XGetGeometry(display, window, &winDummy, &winX, &winY,
                &winWidth, &winHeight, &borderDummy, &depth);
            this->winX = winX;
            this->winY = winY;
            this->winWidth = winWidth;
            this->winHeight = winHeight;
            this->depth = depth;
            this->fullscreen = fullscreen;

            ready = true;
            return true;
        }

        void close()
        {
            if (ready)
            {
                if (renderContext)
                {
                    glXMakeCurrent(display, None, NULL);
                    glXDestroyContext(display, renderContext);
                }
                /* switch back to original desktop resolution if we were in fs */
                if (fullscreen)
                {
                    XF86VidModeSwitchToMode(display, screen, modes[0]);
                    XF86VidModeSetViewPort(display, screen, 0, 0);
                }
                XCloseDisplay(display);

                display = NULL;
                window = NULL;
                renderContext = NULL;
                ready = false;
            }
        }

        inline bool isReady() {return ready;}
        inline bool isFullscreen() {return fullscreen;}

        inline void makeCurrent()
        {
            if (ready)
                glXMakeCurrent(display, window, renderContext);
        }

        bool setFullscreen(bool fullscreen = true)
        {
            if (!ready)
                return false;

            XDestroyWindow(display, window);

            if (fullscreen)
            {
                XF86VidModeSwitchToMode(display, screen, modes[bestMode]);
                XF86VidModeSetViewPort(display, screen, 0, 0);

                window = XCreateWindow(display, RootWindow(display, visual->screen),
                    0, 0, modes[bestMode]->hdisplay, modes[bestMode]->vdisplay, InputOutput, visual->depth, InputOutput, visual->visual,
                    CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect,  &windowAttributes);

                XWarpPointer(display, None, window, 0, 0, 0, 0, 0, 0);
        		XMapRaised(display, window);

                XGrabKeyboard(display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime);
                XGrabPointer(display, window, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
            }
            else
            {
                XF86VidModeSwitchToMode(display, screen, modes[0]);
                XF86VidModeSetViewPort(display, screen, 0, 0);

                window = XCreateWindow(display, RootWindow(display, visual->screen),
                    0, 0, winWidth, winHeight, 0, visual->depth, InputOutput, visual->visual,
                    CWBorderPixel | CWColormap | CWEventMask, &windowAttributes);

                wmDelete = XInternAtom(display, "WM_DELETE_WINDOW", True);
                XSetWMProtocols(display, window, &wmDelete, 1);
                XSetStandardProperties(display, window, caption,
                    caption, None, NULL, 0, NULL);
                XMapRaised(display, window);
            }

            XGetGeometry(display, window, &winDummy, &winX, &winY,
                &winWidth, &winHeight, &borderDummy, &depth);
            this->winX = winX;
            this->winY = winY;
            this->winWidth = winWidth;
            this->winHeight = winHeight;
            this->depth = depth;
            this->fullscreen = fullscreen;

            return true;
        }

        bool update()
        {
            if (!ready)
                return false;

            //if (!drawed)
            //    draw();

            while (XPending(display)>0)
            {
                XNextEvent(display, &event);

                switch (event.type)
                {
                    case MotionNotify:
                    {
                        XMotionEvent *mevent = (XMotionEvent*) &event;
                        cursX = mevent->x;
                        cursY = mevent->y;
                        break;
                    }
                    case KeyPress:
                    {
                        XKeyEvent *kevent = (XKeyEvent*) &event;
                        unsigned char key = (unsigned char)(kevent->keycode);

                        keyH[key] = true;
                        keyD[key] = true;
                        break;
                    }
                    case KeyRelease:
                    {
                        XKeyEvent *kevent = (XKeyEvent*) &event;
                        unsigned char key = (unsigned char)(kevent->keycode);

                        keyD[key] = false;
                        keyH[key] = false;
                        break;
                    }
                    case ButtonPress:
                    {
                        XButtonEvent *bevent = (XButtonEvent*) &event;
                        if (bevent->button >= MOUSE_BUTTON_COUNT)
                            break;

                        mouseD[bevent->button] = 1;
                        mouseH[bevent->button] = 1;
                        break;
                    }
                    case ButtonRelease:
                    {
                        XButtonEvent *bevent = (XButtonEvent*) &event;
                        if (bevent->button >= MOUSE_BUTTON_COUNT)
                            break;

                        mouseD[bevent->button] = 0;
                        mouseH[bevent->button] = 0;
                        break;
                    }
                    case ConfigureNotify:
                        glViewport(0, 0, event.xconfigure.width, event.xconfigure.height);

                        winX = event.xconfigure.x;
                        winY = event.xconfigure.y;
                        winWidth = event.xconfigure.width;
                        winHeight = event.xconfigure.height;
                        break;

                    case Expose:
                        draw();
                        break;
                }
            }

            drawed = false;

            return true;
        }

        bool draw()
        {
            makeCurrent();

            if (doubleBuffering)
                glXSwapBuffers(display, window);
            else
                glFlush();

            drawed = true;

            return true;
        }

        inline void show()
        {
            XMapWindow(display, window);
            XRaiseWindow(display, window);
        }

        inline void hide() {XUnmapWindow(display, window);}

        inline void moveResize(int winX, int winY, uint winWidth, uint winHeight) {XMoveResizeWindow(display, window, winX, winY, winWidth, winHeight);}
        inline void move(int winY, int winH) {XMoveWindow(display, window, winX, winY);}
        inline void resize(uint winWidth, uint winHeight) {XResizeWindow(display, window, winWidth, winHeight);}

        inline bool keyDown(eKey key) {return keyD[key];}
        inline bool keyHit(eKey key)
        {
            if (keyH[key])
            {
                keyH[key] = false;
                return true;
            }
            return false;
        }

        inline char mouseDown(eButton button) {return mouseD[button];}
        inline char mouseHit(eButton button)
        {
            if (mouseH[button])
            {
                mouseH[button] = 0;
                return true;
            }
            return false;
        }

        inline int cursorX() {return cursX;}
        inline int cursorY() {return cursY;}

        inline int getWindowX() {return winX;}
        inline int getWindowY() {return winY;}
        inline uint getWindowWidth() {return winWidth;}
        inline uint getWindowHeight() {return winHeight;}

        inline int getScreenWidth() {return DisplayWidth(display, XDefaultScreen(display));}
        inline int getScreenHeight() {return DisplayHeight(display, XDefaultScreen(display));}
        inline int getScreenWidthMM() {return DisplayWidthMM(display, XDefaultScreen(display));}
        inline int getScreenHeightMM() {return DisplayHeightMM(display, XDefaultScreen(display));}
} Window;

#elif defined(WINDOWS) /// Windows ==============================================================

#include <windows.h>

//#define __USE_BORDERS_NOT_SET_FULLSCREEN__

enum eKeyStateMask
{
    ksNone              = 0,
    ksShift             = 1,
    ksCapsLock          = 2,
    ksControl           = 4,
    ksAlternate         = 8,
    ksNumLock           = 16,
    ksArchi             = 32,       // No idea
    ksSuper             = 64,       // Win
    ksHyper             = 128       // Alt Gr (for italians)
};

enum eKey
{
    kEscape             = 27,
    k1                  = 49,
    k2                  = 50,
    k3                  = 51,
    k4                  = 52,
    k5                  = 53,
    k6                  = 54,
    k7                  = 55,
    k8                  = 56,
    k9                  = 57,
    k0                  = 48,
    kMinus              = 189,
    kEqual              = 187,
    kBackspace          = 8,
    kTab                = 9,
    kQ                  = 'Q',
    kW                  = 'W',
    kE                  = 'E',
    kR                  = 'R',
    kT                  = 'T',
    kY                  = 'Y',
    kU                  = 'U',
    kI                  = 'I',
    kO                  = 'O',
    kP                  = 'P',
    kOpenBracket        = 219,
    kCloseBracket       = 221,
    kEnter              = 13,
    kLControl           = 17,
    kA                  = 'A',
    kS                  = 'S',
    kD                  = 'D',
    kF                  = 'F',
    kG                  = 'G',
    kH                  = 'H',
    kJ                  = 'J',
    kK                  = 'K',
    kL                  = 'L',
    kQuote              = 186,
    kDblQuote           = 222,
    kBackQuote          = 192,
    kLShift             = 16,
    kBackSlash          = 220,
    kZ                  = 'Z',
    kX                  = 'X',
    kC                  = 'C',
    kV                  = 'V',
    kB                  = 'B',
    kN                  = 'N',
    kM                  = 'M',
    kSemicolon          = 188,
    kColon              = 190,
    kSlash              = 191,
    kRShift             = 16,
    kNumStar            = 80,
    kAlt                = 64,
    kSpace              = 32,
    kCapsLock           = 20,
    kF1                 = 112,
    kF2                 = 113,
    kF3                 = 114,
    kF4                 = 115,
    kF5                 = 116,
    kF6                 = 117,
    kF7                 = 118,
    kF8                 = 119,
    kF9                 = 120,
    kF10                = 121,
    kNumLock            = 144,
    kScrollLock         = 145,
    kNum7               = 55,
    kNum8               = 56,
    kNum9               = 57,
    kNumMinus           = 186,
    kNum4               = 85,
    kNum5               = 73,
    kNum6               = 79,
    kNumPlus            = 191,
    kNum1               = 74,
    kNum2               = 75,
    kNum3               = 76,
    kNum0               = 77,
    kNumDecimal         = 190,

    kF11                = 122,
    kF12                = 123,
    kHome               = 36,
    kUp                 = 38,
    kPageUp             = 33,
    kLeft               = 37,

    kRight              = 39,
    kEnd                = 35,
    kDown               = 40,
    kPageDown           = 34,
    kInsert             = 45,
    kDelete             = 46,

    kRControl           = 17,

    kNumSlash           = 48,
    kHyper              = 0,

    kSuper              = 91,

    kMenu               = 93
};

enum eButton
{
    bLeft               = 1,
    bMiddle             = 2,
    bRight              = 3
};

LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

class eWindow
{
	private:
        friend LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

        HDC hDC;
        HGLRC hRC;
        HINSTANCE hInstance;
        MSG msg;

        HWND hWnd;
        char *caption;
        char *wndClass;
        int winX;
        int winY;
        int winWidth;
        int winHeight;
        int colorBuffer;
        int depthBuffer;

        bool fullscreen;
        bool ready;

        bool keyH[KEYBOARD_BUTTON_COUNT], keyD[KEYBOARD_BUTTON_COUNT];
        char mouseH[MOUSE_BUTTON_COUNT], mouseD[MOUSE_BUTTON_COUNT];
        int cursX, cursY, mouseW;

        bool evClose, evMinimize, evMaximize, evRestore, evMove, evSize;

	public:
		eWindow()
		{
		    caption = NULL;
		    wndClass = NULL;
		    //contextInitialized = false;
		    ready = false;

		    for (uint i=0; i<KEYBOARD_BUTTON_COUNT; i++)
		        keyD[i] = keyH[i] = false;
		    for (uint i=0; i<MOUSE_BUTTON_COUNT; i++)
		        mouseD[i] = mouseH[i] = false;
		    cursX = cursY = mouseW = 0;
		    evClose = evMinimize = evMaximize = evRestore = evMove = evSize = false;
		}

        ~eWindow()
        {
            close();
        }

        bool open(const char* caption = "eWindow",
            int winX = 0, int winY = 0,
            uint winWidth = 800, uint winHeight = 600,
            uint depth = 16, bool fullscreen = false)
        {
            if (ready)
                close();

            delete[] this->caption;
            this->caption = new char[strlen(caption)+1];
            strcpy(this->caption, caption);

            delete[] this->wndClass;
            this->wndClass = new char[strlen(caption)+1];
            strcpy(this->wndClass, caption);

            hInstance = GetModuleHandle(NULL);

            DWORD dwStyle;
            DWORD dwExStyle;
            RECT winRect;
            winRect.left = winX;
            winRect.top = winY;
            winRect.right = winX + winWidth;
            winRect.bottom = winY + winHeight;

            WNDCLASS wc;
            wc.style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
            wc.lpfnWndProc = WindowProcedure;
            wc.cbClsExtra = 0;
            wc.cbWndExtra = 0;
            wc.hInstance = hInstance;
            wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
            wc.hCursor = LoadCursor(NULL, IDC_ARROW);
            wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
            wc.lpszMenuName = NULL;
            wc.lpszClassName = wndClass;

            if (!RegisterClass(&wc))
                return false;

            if (fullscreen)
            {
                DEVMODE dm;
                memset(&dm, 0, sizeof(dm));
                dm.dmSize = sizeof(dm);
                dm.dmPelsWidth = winWidth;
                dm.dmPelsHeight = winHeight;
                dm.dmBitsPerPel = colorBuffer;
                dm.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

                if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
                    fullscreen = false;
            }

            this->fullscreen = fullscreen;

            if (fullscreen)
            {
                dwStyle = WS_POPUP;//WS_OVERLAPPEDWINDOW;
                dwExStyle = WS_EX_APPWINDOW;// | WS_EX_WINDOWEDGE;
                AdjustWindowRectEx(&winRect, dwStyle, false, dwExStyle);
            }
            else
            {
                #ifdef __USE_BORDERS_NOT_SET_FULLSCREEN__
                dwStyle = WS_OVERLAPPEDWINDOW;
                #else
                dwStyle = WS_POPUP;
                #endif
                dwExStyle = WS_EX_APPWINDOW;// | WS_EX_WINDOWEDGE;
                AdjustWindowRectEx(&winRect, dwStyle, false, dwExStyle);
            }

            if ( !(hWnd = CreateWindowEx(dwExStyle, this->wndClass, this->caption,
                dwStyle //WS_CAPTION
                //| WS_POPUPWINDOW
                //| WS_VISIBLE
                | WS_CLIPSIBLINGS
                | WS_CLIPCHILDREN
                //| BS_OWNERDRAW
                ,
                fullscreen? 0: winX, fullscreen? 0: winY,
                winWidth, winHeight,
                NULL, NULL, hInstance, NULL)))
            {
                UnregisterClass( this->wndClass, hInstance);
                return false;
            }

            hDC = GetDC(hWnd);

            PIXELFORMATDESCRIPTOR pfd = {
                sizeof(PIXELFORMATDESCRIPTOR),  // size
                1,                              // version
                PFD_SUPPORT_OPENGL |
                PFD_DRAW_TO_WINDOW |
                PFD_DOUBLEBUFFER,               // support double-buffering
                PFD_TYPE_RGBA,                  // color type
                colorBuffer,                    // prefered color depth
                0, 0, 0, 0, 0, 0,               // color bits (ignored)
                1,                              // alpha buffer
                0,                              // alpha bits (ignored)
                1,                              // accumulation buffer
                0, 0, 0, 0,                     // accum bits (ignored)
                depthBuffer,                    // depth buffer
                1,                              // stencil buffer
                0,                              // no auxiliary buffers
                PFD_MAIN_PLANE,                 // main layer
                0,                              // reserved
                0, 0, 0,                        // no layer, visible, damage masks
            };

            int pixelFormat;

            if (!(pixelFormat = ChoosePixelFormat(hDC, &pfd)))
                return false;

            if (!(SetPixelFormat(hDC, pixelFormat, &pfd)))
                return false;

            /*#if defined(__USE_OPENGL_3_0__)
            PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs;
            wglCreateContextAttribs = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");

            if(!wglCreateContextAttribs)
                return false;

            #define GLX_CONTEXT_MAJOR_VERSION_ARB		        0x2091
            #define GLX_CONTEXT_MINOR_VERSION_ARB		        0x2092
            #define GLX_CONTEXT_FLAGS_ARB			            0x2094
            #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB	    0x0002
            int attributes[] = {
                WGL_CONTEXT_MAJOR_VERSION_ARB,  3,
                WGL_CONTEXT_MINOR_VERSION_ARB,  0,
                #ifdef __USE_FORWARD_COMPATIBLE_OPENGL_3_0__
        		GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
                #endif
                0};

            HGLRC hRC = wglCreateContextAttribs(hDC, hRC, attributes);
            if(!hRC)
                return false;
            #else*/
            if (!(hRC = wglCreateContext(hDC)))
                return false;
            //#endif

            if (!(wglMakeCurrent(hDC, hRC)))
                return false;

            ShowWindow(hWnd, SW_SHOW);
            SetForegroundWindow(hWnd);
            SetFocus(hWnd);

            RECT rect;
            GetWindowRect(hWnd, &rect);
            this->winX = rect.left;
            this->winY = rect.top;
            this->winWidth = rect.right - rect.left;
            this->winHeight = rect.bottom - rect.top;

            ready = true;
            return true;
        }

        void close()
        {
            if (ready)
            {
                wglMakeCurrent(hDC, NULL);
                wglDeleteContext(hRC);
                ReleaseDC(hWnd, hDC);

                if(fullscreen)
                {
                    ChangeDisplaySettings( NULL, 0 );
                    ShowCursor( true );
                }

                DestroyWindow(hWnd);
                UnregisterClass(wndClass, hInstance);

                hInstance = NULL;
                hWnd = NULL;
                hDC = NULL;
                hRC = NULL;
            }
        }

        inline bool isReady() {return ready;}
        inline bool isFullscreen() {return fullscreen;}

        inline void makeCurrent() {wglMakeCurrent(hDC, hRC);}

        bool setFullscreen(bool fullscreen = true)
        {
            if (this->fullscreen == fullscreen)
                return false;

            if (fullscreen)
            {
                DEVMODE dm;
                memset(&dm, 0, sizeof(dm));
                dm.dmSize = sizeof(dm);
                dm.dmPelsWidth = winWidth;
                dm.dmPelsHeight = winHeight;
                dm.dmBitsPerPel = colorBuffer;
                dm.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

                if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
                    this->fullscreen = false;
                else
                    this->fullscreen = true;
            }
            else
            {
                if (ChangeDisplaySettings(NULL, 0)!=DISP_CHANGE_SUCCESSFUL)
                    this->fullscreen = true;
                else
                    this->fullscreen = false;
            }

            winX = winY = 0;
            MoveWindow(hWnd, winX, winY, winWidth, winHeight, true);

            return this->fullscreen == fullscreen;
        }

        bool update()
        {
            if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }

            return true;
        }

        bool draw()
        {
            SwapBuffers(hDC);
            return true;
        }

        inline bool keyDown(eKey key) {return keyD[key];}
        inline bool keyHit(eKey key)
        {
            bool tmp = keyH[key];
            keyH[key] = false;
            return tmp;
        }

        inline char mouseDown(eButton button) {return mouseD[button];}
		inline char mouseHit(eButton button)
		{
			bool tmp = mouseH[button];
			mouseH[button] = false;
			return tmp;
		}

        inline int cursorX() {return cursX - winX;}
        inline int cursorY() {return cursY - winY;}

        /*inline bool eventClose();
        inline bool eventMinimize();
        inline bool eventMaximize();
        inline bool eventRestore();
        inline bool eventMove();
        inline bool eventSize();*/

        inline int getWindowX() {return winX;}
        inline int getWindowY() {return winY;}
        inline uint getWindowWidth() {return winWidth;}
        inline uint getWindowHeight() {return winHeight;}

        inline uint getScreenWidth() {return 0;}
        inline uint getScreenHeight() {return 0;}
        inline uint getScreenWidthMM() {return 0;}
        inline uint getScreenHeightMM() {return 0;}
} Window;

LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_MOUSEMOVE:
            Window.cursX = LOWORD(lParam) + Window.winX;
            Window.cursY = HIWORD(lParam) + Window.winY;
            return 0;

        case WM_KEYDOWN:
            Window.keyD[wParam] = true;
            Window.keyH[wParam] = true;
            return 0;
        case WM_KEYUP:
            Window.keyD[wParam] = false;
            Window.keyH[wParam] = false;
            return 0;

        case WM_LBUTTONDOWN:
            Window.mouseH[bLeft] = 1;
            Window.mouseD[bLeft] = 1;
            return 0;
        case WM_LBUTTONUP:
            Window.mouseH[bLeft] = 0;
            Window.mouseD[bLeft] = 0;
            return 0;

        case WM_RBUTTONDOWN:
            Window.mouseH[bRight] = 1;
            Window.mouseD[bRight] = 1;
            return 0;
        case WM_RBUTTONUP:
            Window.mouseH[bRight] = 0;
            Window.mouseD[bRight] = 0;
            return 0;

        case WM_MBUTTONDOWN:
            Window.mouseH[bMiddle] = 1;
            Window.mouseD[bMiddle] = 1;
            return 0;
        case WM_MBUTTONUP:
            Window.mouseH[bMiddle] = 0;
            Window.mouseD[bMiddle] = 0;
            return 0;

        case WM_MOUSEWHEEL:
            Window.mouseW += GET_WHEEL_DELTA_WPARAM(wParam);
            return 0;

        /*case WM_NCHITTEST:
            LRESULT res = DefWindowProc (hWnd, message, wParam, lParam);
            switch (res)
            {
                case HTCLIENT:
                    Window.cursX = LOWORD(lParam);
                    Window.cursY = HIWORD(lParam);
                    break;
            }
            return 0;*/

        /*case WM_SYSCOMMAND:
            switch (wParam)
            {
                case SC_CLOSE:
                    Window.evClose = true; return 0;
                case SC_MAXIMIZE:
                    Window.evMaximize = true; return 0;
                case SC_MINIMIZE:
                    Window.evMinimize = true; return 0;
                case SC_RESTORE:
                    Window.evRestore = true; return 0;
                case SC_MOVE:
                    Window.evMove = true; return 0;
                case SC_SIZE:
                    Window.evSize = true; return 0;
                default:
                    return 0;
            }*/

        default:
            return DefWindowProc (hWnd, message, wParam, lParam);
    }
}

#endif

#endif
